/*
 * 巴法科技:2022-11-22
 * 支持巴法app和小程序配网，长按按键可重新配网
 */
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <WiFiUdp.h>

WiFiClient client_bemfa_WiFiClient;
HTTPClient http_bemfa_HTTPClient;
// 巴法云服务器地址默认即可
#define TCP_SERVER_ADDR "bemfa.com"
// 服务器端口，tcp创客云端口8344
#define TCP_SERVER_PORT "8344"
// 最大字节数
#define MAX_PACKETSIZE 512
// 设置心跳值30s
#define KEEPALIVEATIME 30 * 1000
// tcp客户端相关初始化，默认即可
String TcpClient_Buff = "";
unsigned int TcpClient_BuffIndex = 0; // 储存接收数据
unsigned long TcpClient_preTick = 0;  // 接收数据时间标志
unsigned long preHeartTick = 0;       // 发送心跳时间标志
unsigned long preTCPStartTick = 0;    // 重连连接时间标志

const int buttonPin = 0; // 定义按钮输出引脚
const int LedBlink = 13; // 指示灯输出引脚
const int WenDuPin = 14; // 定义室温输入引脚

// const int LedBlink = 2;  // 指示灯引脚

// 相关函数初始化
void saveConfig();

// 连接WIFI
void initWiFi();
void doWiFiTick();

// TCP初始化连接
void doTCPClientTick();
void startTCPClient();
void doHttpStick(String msg);
void sendtoTCPServer(String p);

String MAC = "4FDD72"; // 增压泵控制器
// String MAC = "C3D283"; // 增压泵控制器
String UID = "4892bd7fe005ecbbccf35929157ec7e7";
String TOPIC1 = MAC + "004";       // 增压泵控制器
String TOPIC2 = MAC + "006";       // 球阀
String wifi_name = "15367660966";  // WIFI名称，区分大小写，不要写错
String wifi_password = "15367660"; // WIFI密码
String Name = "增压泵控制器";      // 设备名称

// 接收端IP地址和端口
IPAddress receiverIP(192, 168, 0, 217);
// IPAddress receiverIP(192, 168, 18, 66);
const uint16_t receiverPort = 8888;
WiFiUDP udp;            // 声明UDP对象
char packetBuffer[255]; // 用于存储UDP数据包的缓冲区

int ShuiWeiValue = 0;       // 水位值
float ShuiWenValue = 0;     // 温度
float ShuiYaValue = 0;      // 水压值
bool System_AB = false;     // 选择A和B系统标志
bool initWiFiState = false; // 网络连接状态

/**
 * 结构体，用于存储配网信息
 */
struct config_type
{
  uint8_t magic;
  bool QF_State = false;   // 球阀状态
  bool Auto_State = false; // 自动模式
  float cm1 = 200;         // 最低位的距离
  float cm2 = 1500;        // 最高位的距离
  float adc1 = 70.0;       // ADC 的下限
  float kpa1 = 0;          // 实际压力的下限
  float adc2 = 1000.0;     // ADC 的上限
  float kpa2 = 450.0;      // 实际压力的上限
};
config_type config;
OneWire oneWire(WenDuPin);
DallasTemperature sensors(&oneWire);

#include <Update.h>
#include <SystemA.h>
#include <SystemB.h>

/**
 * 存储信息
 */
void saveConfig()
{
  EEPROM.begin(256);
  uint8_t *p = (uint8_t *)(&config);
  for (int i = 0; i < int(sizeof(config)); i++)
  {
    EEPROM.write(i, *(p + i));
  }
  EEPROM.commit();
}

/**
 * 加载存储的信息，并检查是否恢复出厂信息
 */
void loadConfig()
{
  EEPROM.begin(256);
  uint8_t *p = (uint8_t *)(&config);
  for (int i = 0; i < int(sizeof(config)); i++)
  {
    *(p + i) = EEPROM.read(i);
  }
  if (config.magic != 0xAA)
  {
    config.magic = 0xAA;
    config.QF_State = false;   // 球阀状态
    config.Auto_State = false; // 自动模式
    saveConfig();
  }
  Serial.print("自动模式:");
  Serial.println(config.Auto_State);
  Serial.print("球阀状态:");
  Serial.println(config.QF_State);
}

/**
 * 初始化wifi信息，并连接路由器网络
 *
 */
void initWiFi()
{
  static int initWiFi = 0;
  if (initWiFi++ >= 3)
  {
    ESP.restart();
  }
  WiFi.disconnect();                    // 断开连接
  WiFi.mode(WIFI_STA);                  // STA模式
  WiFi.begin(wifi_name, wifi_password); // 连接路由器
}

/*
 *发送数据到TCP服务器
 */
void sendtoTCPServer(String p)
{

  if (!client_bemfa_WiFiClient.connected())
  {
    return;
  }
  client_bemfa_WiFiClient.print(p);
  preHeartTick = millis(); // 心跳计时开始，需要每隔60秒发送一次数据
}

/*
 *初始化和服务器建立连接
 */
void startTCPClient()
{
  static int Count = 0;
  static int failCount = 10;
  if (client_bemfa_WiFiClient.connect(TCP_SERVER_ADDR, atoi(TCP_SERVER_PORT)))
  {
    Serial.println("bemfa.com config ok");
    Serial.printf("%s: %d\r\n", TCP_SERVER_ADDR, atoi(TCP_SERVER_PORT));
    String tcpTemp = "";                                                                               // 初始化字符串
    tcpTemp = "cmd=1&uid=" + UID + "&topic=" + TOPIC1 + "" + (System_AB ? "," + TOPIC2 : "") + "\r\n"; // 构建订阅指令
    sendtoTCPServer(tcpTemp);                                                                          // 发送订阅指令
    Serial.println(tcpTemp);
    tcpTemp = ""; // 清空
    client_bemfa_WiFiClient.setNoDelay(true);
    failCount = 0;
    char ipStr[16];
    IPAddress ip = WiFi.localIP();
    ip.toString().toCharArray(ipStr, 16);
    sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC1 + "/up&msg=" + (System_AB ? "控制器" : "增压泵") + " IP: " + ipStr + "\r\n"); // 推送消息
  }
  else
  {
    Count = Count + 1;
    if (failCount < Count) // 如果失败连接10次，重启系统
    {
      // delayRestart(0);
    }
    Serial.print("Failed connected to server:");
    Serial.println(TCP_SERVER_ADDR);
    client_bemfa_WiFiClient.stopAll();
  }
  preTCPStartTick = millis();
}

/*
 *检查数据，发送心跳
 */
void doTCPClientTick()
{
  // 检查是否断开，断开后重连
  if (WiFi.status() != WL_CONNECTED)
  {
    preTCPStartTick = millis();
    return;
  }
  if (!client_bemfa_WiFiClient.connected()) // 断开重连
  {
    if (millis() - preTCPStartTick > 3000) // 重新连接
    {
      startTCPClient();
    }
  }
  else
  {
    if (client_bemfa_WiFiClient.available())
    { // 收数据
      char c = client_bemfa_WiFiClient.read();
      TcpClient_Buff += c;
      TcpClient_BuffIndex++;
      TcpClient_preTick = millis();
      if (TcpClient_BuffIndex >= MAX_PACKETSIZE - 1)
      {
        TcpClient_Buff = "";
        TcpClient_BuffIndex = 0;
        TcpClient_preTick = TcpClient_preTick - 200;
      }
    }
    if (millis() - preHeartTick >= KEEPALIVEATIME)
    {
      sendtoTCPServer("cmd=0&msg=keep\r\n");
    }
    if ((TcpClient_Buff.length() >= 1) && (millis() - TcpClient_preTick >= 10))
    {
      client_bemfa_WiFiClient.flush();
      if (TcpClient_Buff.indexOf("update") > 0)
      {
        Serial.println("[update] Update Start......");
        updateBin();
      }
      else if (TcpClient_Buff.length() >= 60)
      {
        Serial.print("接收数据:" + String(TcpClient_Buff.length()) + "字节" + TcpClient_Buff);
        if (System_AB)
        {
          receiveTCPServerA(TcpClient_Buff);
        }
        else
        {
          receiveTCPServerB(TcpClient_Buff);
        }
      }
      TcpClient_Buff = "";
      TcpClient_BuffIndex = 0;
    }
  }
}

/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上，若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick()
{
  static uint32_t lastWiFiCheckTick = 0;
  static uint32_t ResetWiFiCheckTick = 0;
  // 未连接1s重连
  if (WiFi.status() != WL_CONNECTED)
  {
    if (millis() - ResetWiFiCheckTick > 60000)
    {
      initWiFi();
      ResetWiFiCheckTick = millis();
    }
    if (millis() - lastWiFiCheckTick > 100)
    {
      digitalWrite(LedBlink, !digitalRead(LedBlink)); // 切换指示灯状态
      lastWiFiCheckTick = millis();
      delay(10);
    }
    initWiFiState = false;
  }
  // 连接成功建立
  else
  {
    if (initWiFiState == false)
    {
      Serial.println("");
      Serial.println("wifi config ok");
      Serial.print("IP: ");
      Serial.println(WiFi.localIP());
      digitalWrite(LedBlink, LOW); // 切换指示灯状态
      startTCPClient();
      initWiFiState = true;
    }
  }
}

//  微信消息推送函数
void doHttpStick(String msg)
{
  String type = "2"; // 1表示是预警消息，2表示设备提醒消息
  WiFiClient WiFi;
  HTTPClient http;
  String ApiUrl = "http://api.bemfa.com/api/wechat/v1/"; // 默认 api 网址
  String postData = "uid=" + UID + "&type=" + type + "&device=" + Name + "&msg=" + msg;
  http.begin(WiFi, ApiUrl);                                            // Specify request destination
  http.addHeader("Content-Type", "application/x-www-form-urlencoded"); // Specify content-type header
  http.POST(postData);                                                 // Send the request
  http.end();                                                          // Close connection
}

// 初始化，相当于main 函数
void setup()
{
  Serial.begin(9600);
  Serial.println("");
  Serial.println("Beginning...");
  pinMode(buttonPin, INPUT);
  pinMode(LedBlink, OUTPUT);
  digitalWrite(LedBlink, LOW);
  initWiFi();
  loadConfig();
  sensors.begin();
  sensors.requestTemperatures();
  String mac = WiFi.macAddress().substring(8); // 取mac地址做主题用
  mac.replace(":", "");                        // 去掉:号
  System_AB = mac == MAC;
  if (System_AB)
  { // 初始化A系统
    Serial.println("初始化A系统...");
    setupSystemA();
  }
  else
  { // 初始化B系统
    Serial.println("初始化B系统...");
    setupSystemB();
  }
}
// 循环
void loop()
{
  doWiFiTick();      // 检查wifi
  doTCPClientTick(); // tcp消息接收
  if (System_AB)
  {
    loopSystemA();
  }
  else
  {
    loopSystemB();
  }
}
